package com.bjy.qa.controller.performancetest;

import com.alibaba.fastjson.JSONObject;
import com.bjy.qa.entity.MyPage;
import com.bjy.qa.entity.Response;
import com.bjy.qa.entity.ResponsePagingData;
import com.bjy.qa.entity.functionaltest.TestSuite;
import com.bjy.qa.entity.performancetest.LatestDataDTO;
import com.bjy.qa.entity.performancetest.PerfTestResult;
import com.bjy.qa.entity.performancetest.PerfTestResultLog;
import com.bjy.qa.entity.performancetest.ReportDTO;
import com.bjy.qa.enumtype.ErrorCode;
import com.bjy.qa.enumtype.PerfLogType;
import com.bjy.qa.exception.MyException;
import com.bjy.qa.service.performancetest.IPerfTestResultService;
import com.bjy.qa.service.user.IUserCompanyProjectService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;

@Controller
@RequestMapping("/pt/perfTestResult")
@Validated
@Api(tags = "性能测试结果")
public class PerfTestResultController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final static ConcurrentLinkedQueue<PerfTestResultLog> perfTestResultLogQueue = new ConcurrentLinkedQueue<>(); // 性能测试结果日志 队列
    private static long perfTestResultLogQueueLock = 0; // 性能测试结果日志 队列 锁

    @Resource
    IUserCompanyProjectService iUserCompanyProjectService;

    @Resource
    IPerfTestResultService iPerfTestResultService;

    @ResponseBody
    @RequestMapping(value = "/list", method = {RequestMethod.POST})
    @ApiOperation(value = "分页查询性能测试结果列表", notes = "分页查询性能测试结果列表")
    public Response<ResponsePagingData> list(@RequestBody MyPage<PerfTestResult> myPage) throws Exception {
        logger.info("分页查询性能测试结果列表：{}", myPage);
        Response response = Response.success(iPerfTestResultService.list(myPage));
        logger.info("分页查询性能测试结果列表，成功 {}", response);
        return response;
    }

    @ResponseBody
    @RequestMapping(value = "/getPerfTestResultInfoById", method = {RequestMethod.GET})
    @ApiOperation(value = "根据 id 查询 性能测试结果信息", notes = "根据 id 查询 性能测试结果信息")
    public Response<TestSuite> getPerfTestResultInfoById(@NotNull(message = "projectId: 字段必填！") Long projectId, @NotNull(message = "ID: id字段必填！") Long id) {
        logger.info("根据 id 查询 性能测试结果信息，projectId: {}, ID: {}", projectId, id);
        Response response;
        if (!iUserCompanyProjectService.hasUserProjectPermissions(null, projectId)) {
            response = Response.fail(ErrorCode.UNAUTHORIZED, String.format("您没有当前项目（projectId: %d）权限，请刷新页面后重试！", projectId));
        } else {
            response = Response.success(iPerfTestResultService.getPerfTestResultInfoById(id));
        }
        logger.info("根据 id 查询 性能测试结果信息，成功 {}", response);
        return response;
    }

    @ResponseBody
    @RequestMapping(value = "/getCaseResultDetails", method = {RequestMethod.GET})
    @ApiOperation(value = "查询测试结果中的某个测试脚本的日志", notes = "查询测试结果中的某个测试脚本的日志")
    public Response<List<PerfTestResultLog>> getCaseResultDetails(@NotNull(message = "resultId: 测试结果 ID 字段必填！") Long resultId, @NotNull(message = "cid: 测试脚本 ID 字段必填！") Long testScriptId) {
        logger.info("查询测试结果中的某个测试脚本的日志 resultId: {}, testScriptId: {}", resultId, testScriptId);
        Response response = Response.success(iPerfTestResultService.getCaseResultDetails(resultId, testScriptId));
        logger.info("查询测试结果中的某个测试脚本的日志，成功。{}", response);
        return response;
    }

    @ResponseBody
    @RequestMapping(value = "/getReport", method = {RequestMethod.GET})
    @ApiOperation(value = "查询测试结果 - 测试报告", notes = "查询测试结果 - 测试报告")
    public Response<ReportDTO> getReport(@NotNull(message = "resultId: 测试结果 ID 字段必填！") Long resultId, @NotNull(message = "cid: 测试脚本 ID 字段必填！") Long testScriptId) {
        logger.info("查询测试结果 - 测试报告 resultId: {}, testScriptId: {}", resultId, testScriptId);
        Response response = Response.success(iPerfTestResultService.getReport(resultId, testScriptId));
        logger.info("查询测试结果 - 测试报告，成功。{}", response);
        return response;
    }

    @ResponseBody
    @RequestMapping(value = "/getLatestData", method = {RequestMethod.GET})
    @ApiOperation(value = "查询 性能测试实时图表页签 - 表格中显示的数据", notes = "查询 性能测试实时图表页签 - 表格中显示的数据")
    public Response<LatestDataDTO> getLatestData(@NotNull(message = "resultId: 测试结果 ID 字段必填！") Long resultId, @NotNull(message = "cid: 测试脚本 ID 字段必填！") Long testScriptId) {
        logger.info("查询 性能测试实时图表页签 - 表格中显示的数据 resultId: {}, testScriptId: {}", resultId, testScriptId);
        Response response = Response.success(iPerfTestResultService.getLatestData(resultId, testScriptId));
        logger.info("查询 性能测试实时图表页签 - 表格中显示的数据，成功。{}", response);
        return response;
    }

    @ResponseBody
    @RequestMapping(value = "/getChart", method = {RequestMethod.GET})
    @ApiOperation(value = "查询 性能测试实时图表页签 - chart 绘图需要的数据", notes = "查询 性能测试实时图表页签 - chart 绘图需要的数据")
    public Response<LatestDataDTO> getChart(@NotNull(message = "resultId: 测试结果 ID 字段必填！") Long resultId, @NotNull(message = "cid: 测试脚本 ID 字段必填！") Long testScriptId) {
        logger.info("查询 性能测试实时图表页签 - chart 绘图需要的数据 resultId: {}, testScriptId: {}", resultId, testScriptId);
        Response response = Response.success(iPerfTestResultService.getChart(resultId, testScriptId));
        logger.info("查询 性能测试实时图表页签 - chart 绘图需要的数据，成功。{}", response);
        return response;
    }

    @RequestMapping(value = "/log", method = {RequestMethod.POST})
    @ResponseBody
    @ApiOperation(value = "性能测试结果上报", notes = "性能测试结果上报")
    public Response<String> log(@RequestBody String body) {
        JSONObject bodyJson = JSONObject.parseObject(body);

        // 参数校验
        Integer ctype = bodyJson.getInteger("ctype");
        if (ctype == null) {
            throw new MyException("ctype: 分类类型 - 上报日志类型 必填");
        }
        Long rid = bodyJson.getLong("rid");
        if (rid == null) {
            throw new MyException("rid: 测试结果 id 必填");
        }
        Long cid = bodyJson.getLong("cid");
        if (cid == null) {
            throw new MyException("cid: 测试脚本 id 必填");
        }
        Integer status = bodyJson.getInteger("status");
        if (status == null) {
            throw new MyException("status: 状态 必填");
        }
        String msg = bodyJson.getString("msg");
        if (StringUtils.isEmpty(msg)) {
            throw new MyException("msg: msg (step、status) 必填");
        }

        // 将 body 转为 PerfTestResultLog
        PerfTestResultLog perfTestResultLog = new PerfTestResultLog();
        perfTestResultLog.setCtype(ctype);
        perfTestResultLog.setResultId(rid);
        perfTestResultLog.setTestScriptId(cid);
        perfTestResultLog.setDesc(bodyJson.getString("desc"));
        perfTestResultLog.setType(bodyJson.getString("type"));
        perfTestResultLog.setLog(bodyJson.getString("log"));
        perfTestResultLog.setStatus(status);
        perfTestResultLog.setMsg(msg);

        if (perfTestResultLog.getDesc() != null) {
            if (perfTestResultLog.getDesc().equals(String.valueOf(PerfLogType.LOG_REQUESTS.getValue()))) {
                perfTestResultLog.setDesc(PerfLogType.LOG_REQUESTS.getName());
                perfTestResultLog.setLogType(PerfLogType.LOG_REQUESTS.getValue());
            } else if (perfTestResultLog.getDesc().equals(String.valueOf(PerfLogType.LOG_TASKS.getValue()))) {
                perfTestResultLog.setDesc(PerfLogType.LOG_TASKS.getName());
                perfTestResultLog.setLogType(PerfLogType.LOG_TASKS.getValue());
            } else if (perfTestResultLog.getDesc().equals(String.valueOf(PerfLogType.LOG_EXCEPTIONS.getValue()))) {
                perfTestResultLog.setDesc(PerfLogType.LOG_EXCEPTIONS.getName());
                perfTestResultLog.setLogType(PerfLogType.LOG_EXCEPTIONS.getValue());
            } else if (perfTestResultLog.getDesc().equals(String.valueOf(PerfLogType.CSV_REQUESTS.getValue()))) {
                perfTestResultLog.setDesc(PerfLogType.CSV_REQUESTS.getName());
                perfTestResultLog.setLogType(PerfLogType.CSV_REQUESTS.getValue());
            } else if (perfTestResultLog.getDesc().equals(String.valueOf(PerfLogType.CSV_EXCEPTIONS.getValue()))) {
                perfTestResultLog.setDesc(PerfLogType.CSV_EXCEPTIONS.getName());
                perfTestResultLog.setLogType(PerfLogType.CSV_EXCEPTIONS.getValue());
            } else if (perfTestResultLog.getDesc().equals(String.valueOf(PerfLogType.CSV_FAILURES.getValue()))) {
                perfTestResultLog.setDesc(PerfLogType.CSV_FAILURES.getName());
                perfTestResultLog.setLogType(PerfLogType.CSV_FAILURES.getValue());
            }
        }

        logger.info("性能测试结果上报: {}", perfTestResultLog);

        perfTestResultLog.setUpdatedAt(new Date());
        perfTestResultLog.setCreatedAt(new Date());
        perfTestResultLogQueue.add(perfTestResultLog); // 将日志添加到 性能测试结果日志 队列 中

        Response response = Response.success("测试结果处理成功");
        logger.info("性能测试结果上报，成功 {}", response);
        return response;
    }

    /**
     * 保存性能测试结果日志 任务
     */
    @Scheduled(fixedRate = 200) // 每 200 毫秒执行一次
    public void perfLogTask() {
        if (perfTestResultLogQueueLock == 0) {
            PerfTestResultLog perfTestResultLog = perfTestResultLogQueue.poll();
            if (perfTestResultLog != null) {
                try {
                    logger.info("保存从 性能测试结果日志队列 中获取到的日志");
                    perfTestResultLogQueueLock = System.currentTimeMillis() + 3000; // 加锁（非 0 上锁。用当前时间 + 3s 当超时时间）

                    iPerfTestResultService.log(perfTestResultLog);
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error("保存从 性能测试结果日志队列 中获取到的日志 错误：" + e.getMessage());
                } finally {
                    perfTestResultLogQueueLock = 0; // 解锁
                }
            }
        } else {
            if (perfTestResultLogQueueLock < System.currentTimeMillis()) {
                logger.error("性能测试结果日志队列锁 超时，强制解锁");
                perfTestResultLogQueueLock = 0; // 锁定超时后，解锁
            }
        }
    }
}
